The MaskEdBox control is a TextBox-like control with many additional features that are helpful, or even necessary, in building robust and bulletproof data entry procedures. This control is embedded in the MSMask32.ocx, so you have to distribute this file with all the applications that include one or more instances of the MaskEdBox control.
All the custom properties of the MaskEdBox control can be set in the General tab of the custom Property Pages dialog box, as shown in Figure 12-1. The MaxLength property is the maximum number of characters accepted in the control; if the AutoTab property is True, the focus automatically advances to the following field after the user has typed the allotted number of characters. The PromptChar property sets the prompt character, that is, the symbol used as a placeholder for an input character. (The default is the underscore character.) The AllowPrompt Boolean property determines whether the prompt character is also a valid input character. (The default is False.) The PromptInclude property determines whether the Text property returns prompt characters.
Figure 12-1. The MaskEdBox control at design time.
The key property of the MaskEdBox control is Mask, a string that tells you which characters are allowed in each position of the control's contents. This string can include special characters that specify whether the required character is a digit, a letter, a decimal or thousand separator, or another kind of character. (See Table 12-1 for a complete list of special characters.) For example, the following statement prepares a MaskEdBox control for accepting a phone number:
MaskEdBox1.Mask = "(###)###-####" |
You can specify a date and time format using the appropriate separators, as shown here:
MaskEdBox1.Mask = "##/##/##" ' A date value in mm/dd/yy format MaskEdBox1.Mask = "##:##" ' A time value in hh:mm format |
In this case, however, the MaskEdBox control performs only a character-by-character validation, and it's up to you to check that the control contains a valid date in the Validate event procedure. For this reason, it's usually preferable to use a DateTimePicker control for date and time entry because this control performs all the validation chores automatically. If the Mask property is assigned an empty string, the control behaves like a regular TextBox control.
Table 12-1. Special characters in the Mask property of a MaskEdBox control. The actual characters accepted for decimal, thousand, date, and time separators depend on the localization settings of your system.
Character | Description |
---|---|
# | Required digit. |
9 | Optional digit. |
. | Decimal separator. |
, | Thousand separator. |
: | Time separator. |
/ | Date separator. |
& | Character placeholder. (All ANSI codes, except nonprintable characters, such as the tab character.) |
C | Same as &. (Ensures compatibility with Microsoft Access.) |
A | Alphanumerical required character (a_z, A_Z, 0_9). |
a | Alphanumerical optional character (a_z, A_Z, 0_9). |
? | Letter placeholder (a_z, A_Z). |
> | Convert all characters that follow to uppercase. |
< | Convert all characters that follow to lowercase. |
\ | Escape symbol: the character or symbol that follows is treated as a literal. |
(Other) | Any other character in the mask is treated as a literal and is displayed in the control as is. |
The Format property determines the appearance of the control when the focus leaves it. For example, you might have a date field that should be formatted as a long date when the user leaves the control. You can arrange this by assigning the Format property a suitable string either at design time or at run time:
MaskEdBox1.Format = "mmmm dd, yyyy" |
You can pass the Format property any value that you would use with the VBA's Format function, except named formats such as scientific or long date. You can also pass up to four values to format positive, negative, zero, or Null values with different format substrings separated by a semicolon, as in this code:
' Show two decimal digits and the thousand separator, enclose ' negative numbers within parentheses, and show "Zero" when "0" ' has been entered. MaskEdBox1.Format = "#,##0.00;(#,##0.00);Zero" |
The fourth format substring is used when the control is bound to a database field that contains the Null value.
The only other custom property that you can set at design time is ClipMode, which determines what happens when the user copies or cuts the contents of the control to the Clipboard. If this property is 0-mskIncludeLiterals, the string being cut or copied includes all literal characters. If the property is 1-mskExcludeLiterals, all literal characters are filtered out before cutting or copying the string to the Clipboard. This property has no effect if Mask is an empty string.
The MaskEdBox control is basically a supercharged TextBox control, and as such it supports many of the latter control's properties, methods, and events. There are a few differences, however, that you should take into account.
The MaskEdBox control supports the Text property, as well as related properties such as SelStart, SelLength, and SelText. The Text property returns the current contents of the control, including all literal characters, separators, and the underscores that are part of the mask. To filter out such extra characters, you can use the value returned by the ClipText read-only property:
' Work with a date control. MaskEdBox1.Mask = "##/##/####" ' Assign a date using the Text property. MaskEdBox1.Text = "12/31/1998" ' Read it back with the ClipText property. Print MaskEdBox1.ClipText ' Displays "1231998" |
Don't forget that when you assign a value to the Text property, you're subject to the same constraints enforced when a string is entered in the control and that assigning an invalid string raises an error.
Fortunately, to retrieve the contents of a MaskEdBox control, you don't have to filter out separators yourself. When the ClipMode property is True, the value returned by the SelText property doesn't include literals and separators. Even more interesting, if you assign a value to this property, the control behaves as if the string had been pasted from the Clipboard or, if you prefer, as if each character had been manually typed in the control. This means that you don't have to include any literals or separators in your assignment statement:
' Read the contents of the control without any separator. MaskEdBox1.ClipMode = mskExcludeLiterals MaskEdBox1.SelStart = 0: MaskEdBox1.SelLength = 9999 MsgBox "The control's value is " & MaskEdBox1.SelText ' Assign a new date value. (Don't worry about date separators.) MaskEdBox1.SelText = "12311998" |
Another way to access the contents of a MaskEdBox control is through the FormattedText property, which returns the string that's displayed in the control when it doesn't have the input focus. If the Mask property is an empty string, this property is similar to the Text property, except that it's read-only. Note that if the HideSelection property has been set to False, the control doesn't format its contents when it loses the input focus; in this case, however, you can still retrieve the formatted value through the FormattedText property.
The MaskEdBox control raises a ValidationError event each time the user presses an invalid key or pastes an invalid string into the control. This event receives two parameters: InvalidText is the value that the Text property would assume if the invalid key had been accepted, and StartPosition is the index of the first invalid character in this string.
Private Sub MaskEdBox1_ValidationError(InvalidText As String, _ StartPosition As Integer) ' StartPosition is zero-based. LblStatus.Caption = "'" & Mid$(InvalidText, StartPosition + 1, 1) _ & "' is an invalid character" End Sub |
The previous code snippet has a defect, though, in that it doesn't work correctly if the invalid character is typed at the end of the current contents of the control. In that case, the Mid$ function returns an empty string, and there's no way to retrieve the invalid character. For this reason, you might prefer to display a generic error message that doesn't contain the actual invalid character.
A shortcoming of the ValidationError event is that it seems impossible to show a message box from within it. If you try to show a message box, you're caught in an endless loop and the event is repeatedly invoked, until you press the Ctrl+Break key combination. (If you're working with a compiled application, you have to force its termination using Ctrl+Alt+Del.)
Starting with Visual Basic 6, the MaskEdBox control supports the standard Validate event, so it's now considerably simpler to enforce data entry validation.